﻿using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Routing;
using SsoaFramework.Core.Behaviors;
using SsoaFramework.Core.Other;

namespace SsoaFramework.Core
{
    public class ServiceRouter
    {
        private readonly ServiceHost routingService = new ServiceHost(typeof(RoutingService));
        private readonly ContractDescription contract = ContractDescription.GetContract(typeof(IRequestReplyRouter));

        public ServiceRegistry ServiceRegistry { get; private set; }
        public RoutingTable RoutingTable { get; private set; }

        public Uri Address { get; private set; }
        public Uri ServicesAddress { get { return new Uri(Address + "Services/"); } }
        public Uri MetadataAddress { get { return new Uri(Address + "Metadata/"); } }

        public ServiceRouter(Uri address, EnterpriseServiceBus esb)
        {
            this.Address = address;
            this.ServiceRegistry = new ServiceRegistry(ServicesAddress, MetadataAddress, esb);
            this.RoutingTable = new RoutingTable(ServiceRegistry, esb);
            ServiceRegistry.NewServiceFound += new EventHandler<NewServiceFoundEventArgs>(ServiceRegistry_NewServiceFound);

            InitializeRouter();
        }

        void ServiceRegistry_NewServiceFound(object sender, NewServiceFoundEventArgs e)
        {
            RoutingTable.Route(e.Service);
        }

        public void Start()
        {
            Log.Instance.Info("Router starting");
            routingService.Open();
        }

        public void Stop()
        {
            Log.Instance.Info("Router stopping");
            routingService.Close();
        }

        private void InitializeRouter()
        {
            AddRoutingMetadataEndpoint(MetadataExchangeBindings.CreateMexHttpBinding());
            AddRoutingServiceEndpoint(new WSHttpBinding());

            bool isDebugBehaviorExist = routingService.Description.Behaviors.Any(b => b.GetType() == typeof (ServiceDebugBehavior));
            if (isDebugBehaviorExist)
                routingService.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
            else
                routingService.Description.Behaviors.Add(new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });

            routingService.Description.Behaviors.Add(new RoutingBehavior(new RoutingConfiguration()));
            routingService.Description.Behaviors.Add(new UpdateRoutingBehavior(RoutingTable));
        }

        private void AddRoutingServiceEndpoint(Binding binding)
        {
            ServiceEndpoint endpoint = new ServiceEndpoint(contract, binding, new EndpointAddress(ServicesAddress));
            routingService.AddServiceEndpoint(endpoint);
        }

        private void AddRoutingMetadataEndpoint(Binding metadataExchangeBinding)
        {
            ServiceEndpoint endpoint = new ServiceEndpoint(contract, metadataExchangeBinding, new EndpointAddress(MetadataAddress));
            routingService.AddServiceEndpoint(endpoint);
        }

    }
}
